/*
 * tmr.c
 *
 * Created: 6/13/2013 3:28:03 PM
 *  Author: Tim
 */ 

//use to gain access to Xmega registers
#include <avr/io.h>
#include <avr/interrupt.h>
#include <math.h>

#include "tmr.h"

#include "../../board/board.h"

#include "../../packets/handlers/adcPacketHandler.h"
#include "../../packets/handlers/dacPacketHandler.h"
#include "../../packets/handlers/encPacketHandler.h"
#include "../dio/dio.h"

//sets a timer interrupt to occurs at a specified frequency
status_t tmrSetupFreq(uint16_t freq)
{
	
	uint32_t clockCycles = round(F_CPU/freq);
	uint8_t  division;
	
	if		(clockCycles > UINT16_MAX * 1024)	
	{
		clockCycles = round(clockCycles/1024);
		division = TC_CLKSEL_DIV1024_gc;	
	}
	else if (clockCycles > UINT16_MAX * 256)	
	{
		clockCycles = round(clockCycles/256);
		division = TC_CLKSEL_DIV256_gc;	
	}
	else if (clockCycles > UINT16_MAX * 64)		
	{
		clockCycles = round(clockCycles/64);
		division = TC_CLKSEL_DIV64_gc;	
	}
	else if (clockCycles > UINT16_MAX * 8)		
	{
		clockCycles = round(clockCycles/8);
		division = TC_CLKSEL_DIV8_gc;	
	}
	else if (clockCycles > UINT16_MAX * 4)
	{
		clockCycles = round(clockCycles/4);
		division = TC_CLKSEL_DIV4_gc;	
	}
	else if (clockCycles > UINT16_MAX * 2)		
	{
		clockCycles = round(clockCycles/2);
		division = TC_CLKSEL_DIV2_gc;	
	}
	else
	{
		division = TC_CLKSEL_DIV1_gc;			
	}

	TCD1.CTRLA = division;
	TCD1.PER   = clockCycles;
	daqAlloc.timerAlloc.timerD1Alloc = FEATURE_UTILIZED;
	return OPP_SUCCESSFUL;
}

void timerInterruptHandler()
{	
	uint8_t packet[packetSizeLookup(inputSuperPacket_mh)];
	uint8_t pos = 2;
	
	WORD_t		temp;
	LONG_WORD_t	temp32;
	
	//turn off data register empty interrupt while in timer interrupt
	//this will just mean data will accumulate int he tx buffer
	USARTD1.CTRLA &= ~USART_DREINTLVL_gm;
	
	packet[0] = packetHeader;
	packet[1] = inputSuperPacket_mh;
	
	//ANALOG
	if(daqAlloc.analogAlloc.analogInCh0Alloc == FEATURE_UTILIZED)
	{
		temp.word = filterData(&adcCh0_data) + daqCal.ADC_CH_OFFSET[0];
		packet[pos++] = temp.bytes.byteH;
		packet[pos++] = temp.bytes.byteL;
	}
	if(daqAlloc.analogAlloc.analogInCh1Alloc == FEATURE_UTILIZED)
	{
		temp.word = filterData(&adcCh1_data) + daqCal.ADC_CH_OFFSET[1];
		packet[pos++] = temp.bytes.byteH;
		packet[pos++] = temp.bytes.byteL;
	}
	if(daqAlloc.analogAlloc.analogInCh2Alloc == FEATURE_UTILIZED)
	{
		temp.word = filterData(&adcCh2_data) + daqCal.ADC_CH_OFFSET[2];
		packet[pos++] = temp.bytes.byteH;
		packet[pos++] = temp.bytes.byteL;
	}
	if(daqAlloc.analogAlloc.analogInCh3Alloc == FEATURE_UTILIZED)
	{
		temp.word = filterData(&adcCh3_data) + daqCal.ADC_CH_OFFSET[3];
		packet[pos++] = temp.bytes.byteH;
		packet[pos++] = temp.bytes.byteL;
	}
		
	//ENCODER
	if(daqAlloc.encAlloc.enc0Alloc == FEATURE_UTILIZED)
	{
		//if we are talking abs encoders we only need a resolution of 16-bit
		if(daqAlloc.encAlloc.enc0Abs == 1)
		{
			temp.word = TCC0.CNT;
			packet[pos++] = temp.bytes.byteH;
			packet[pos++] = temp.bytes.byteL;
		}
		//for incremental encoders we may want more resolution so we will use a 32-bit number
		else
		{
			temp32.long_word = encCount0_32;
			
			if(TCC0.CTRLFSET & TC0_DIR_bm)	temp32.long_word -= TCC0.CNT;
			else							temp32.long_word += TCC0.CNT;
			
			 packet[pos++] = temp32.bytes.byteHH;
			 packet[pos++] = temp32.bytes.byteMH;
			 packet[pos++] = temp32.bytes.byteML;
			 packet[pos++] = temp32.bytes.byteLL;
		}
		
	}	
	if(daqAlloc.encAlloc.enc1Alloc == FEATURE_UTILIZED)
	{
		//if we are talking abs encoders we only need a resolution of 16-bit
		if(daqAlloc.encAlloc.enc1Abs == 1)
		{
			temp.word = TCC1.CNT;
			packet[pos++] = temp.bytes.byteH;
			packet[pos++] = temp.bytes.byteL;
		}
		//for incremental encoders we may want more resolution so we will use a 32-bit number
		else
		{
			temp32.long_word = encCount1_32;
			
			if(TCC1.CTRLFSET & TC1_DIR_bm)	temp32.long_word -= TCC1.CNT;
			else							temp32.long_word += TCC1.CNT;
			
			packet[pos++] = temp32.bytes.byteHH;
			packet[pos++] = temp32.bytes.byteMH;
			packet[pos++] = temp32.bytes.byteML;
			packet[pos++] = temp32.bytes.byteLL;
		}	
	}
	
	// DIGITAL INPUTS
	if(daqAlloc.digitalIn == FEATURE_UTILIZED)
	{
		packet[pos++] = dioReadHigh();
		packet[pos++] = dioReadLow();
	}
	
	crcAppend(&packet,pos + 2);
	comPortPut(&packet,pos + 2);
	//enable DRE to send any data that may have accumulated in the TX buf
	//if there is nothing there no worries because the interrupt will disable itself
	USARTD1.CTRLA |= USART_DREINTLVL_LO_gc;		
	
}

ISR(TCD1_OVF_vect)
{
	timerInterruptHandler();
}

